package mips.instructions;

import java.util.ArrayList;
import java.util.List;

import mips.structure.MipsFunc;
import mips.structure.MipsInstr;
import mips.value.MipsValue;
import mips.value.Register;

public class Jal extends MipsInstr {
    List<MipsValue> args = new ArrayList<>();
    String name;
    int offset;
    MipsFunc func;
    int stackOffset;
    List<Register> savedRegisters = new ArrayList<>();

    public Jal(String name, int offset, MipsFunc func) {
        this.name = name;
        this.offset = -offset + 16;
        this.func = func;
        this.stackOffset = func.getStackSize();
        this.savedRegisters = new ArrayList<>(func.getUsedRegisters());
    }

    public Jal(String name, List<MipsValue> args, int offset, MipsFunc func) {
        this.name = name;
        this.args = args;
        this.offset = -offset + 16;
        this.func = func;
        this.stackOffset = func.getStackSize();
        this.savedRegisters = new ArrayList<>(func.getUsedRegisters());
    }

    @Override
    public String toString() {
        // 在调用函数时，需要将参数放入寄存器中
        StringBuilder sb = new StringBuilder();
        // 对于小于参数寄存器的个数的参数，放入参数寄存器中
        for (int i = 0; i < args.size() && i < Register.getArgumentegistersCount(); i++) {
            if (args.get(i) instanceof Register)
                sb.append("move ").append(Register.getArgumentRegisters().get(i)).append(", ").append(args.get(i))
                        .append("\n");
            else
                sb.append("li ").append(Register.getArgumentRegisters().get(i)).append(", ").append(args.get(i))
                        .append("\n");
        }
        // 对于大于参数寄存器的个数的参数，放入栈中
        for (int i = Register.getArgumentegistersCount(); i < args.size(); i++) {
            if (args.get(i) instanceof Register) {
                sb.append("sw ").append(args.get(i)).append(", ").append(offset).append("($sp)\n");
            } else {
                sb.append("li $v1, ").append(args.get(i)).append("\n");
                sb.append("sw $v1, ").append(offset).append("($sp)\n");
            }
            offset += 4;
        }
        // 调用函数
        if (name.equals("putint"))
            sb.append("li $v0, 1\nsyscall\n");
        else if (name.equals("putch"))
            sb.append("li $v0, 11\nsyscall\n");
        else if (name.equals("getint"))
            sb.append("li $v0, 5\nsyscall\n");
        else if (name.equals("getchar"))
            sb.append("li $v0, 12\nsyscall\n");
        else if (name.equals("putstr"))
            sb.append("li $v0, 4\nsyscall\n");
        else {
            // 将函数正在使用的寄存器保存
            for (Register register : savedRegisters) {
                sb.append("sw ").append(register).append(", ")
                        .append(stackOffset + Register.TEMP_REGISTER_OFFSET.get(register))
                        .append("($sp)\n");
            }
            sb.append("jal ").append(name).append("\n");
            // 将保存的寄存器恢复
            for (Register register : savedRegisters) {
                sb.append("lw ").append(register).append(", ").append(stackOffset + Register.TEMP_REGISTER_OFFSET
                        .get(register)).append("($sp)\n");
            }
        }
        return sb.toString();
    }
}
